﻿using System.Collections.Generic;
using MentalAlchemy.Atomics;

namespace MentalAlchemy.Molecules.MachineLearning.GradEAAlg
{
	public class Individual : MachineLearning.Individual
	{
		public const float DEFAULT_UPDATE_INERTIA = 0.1f;

		#region - Property. -
		public List<float[]> LastChange { get; protected set; }

		/// <summary>
		/// Parameter, which is used for intertia update.
		/// delta(t) = delta(t) + [UpdateInertia] * delta(t-1)
		/// </summary>
		public float UpdateInertia { get; set; } 
		#endregion

		#region - Constructors. -
		public Individual()
		{
			LastChange = new List<float[]>();
			UpdateInertia = DEFAULT_UPDATE_INERTIA;
		}

		public Individual(Individual ind)
			: base(ind)
		{
			LastChange = new List<float[]>(ind.LastChange);
			UpdateInertia = ind.UpdateInertia;
		} 
		#endregion

		public void ApplyChanges (float[] delta)
		{
			var d = delta;

			//var changeCount = LastChange.Count;
			var changeCount = LastChange.Count;
			// if there were changes and the last change is collinear to [delta].
			if (changeCount > 0 && VectorMath.DotProduct(LastChange[changeCount - 1], d) > 0)
			{
				var offset = VectorMath.Mul(LastChange[changeCount - 1], UpdateInertia);
				d = VectorMath.Add(d, offset);
				LastChange[0] = d;	// register current change.
			}
			else
			{
				LastChange.Add(d);	// register current change.
			}

			Genes = new List<float>(VectorMath.Add(Genes.ToArray(), d));
		}

		#region - Overrides. -
		public override AbstractIndividual Clone()
		{
			return new Individual(this);
		}

		public override void Init(int size, System.Random rand, float range, float min)
		{
			base.Init(size, rand, range, min);
			LastChange.Clear();
		}

		public Individual Assign(Individual ind)
		{
			base.Assign(ind);

			LastChange = new List<float[]>(ind.LastChange);
			UpdateInertia = ind.UpdateInertia;

			return this;
		}
		#endregion
	}
}
